package com.icloud.article.boot.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.icloud.article.boot.config.SdkConfig;
import com.icloud.article.boot.dto.*;
import com.icloud.article.boot.service.IMService;
import com.icloud.article.boot.vo.*;
import com.icloud.common.cache.constants.CacheConstants;
import com.icloud.common.cache.service.RedisService;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.database.enums.TencentIMUserTagEnum;
import com.icloud.common.database.mapper.AppCustomerMapper;
import com.icloud.common.database.model.AppCustomer;
import com.icloud.common.web.exception.ApiException;
import com.icloud.common.web.exception.Asserts;
import com.icloud.common.web.util.AuthUtil;
import com.icloud.common.web.util.TLSSigAPIv2;
import io.github.doocs.im.ImClient;
import io.github.doocs.im.model.group.GroupInfo;
import io.github.doocs.im.model.request.*;
import io.github.doocs.im.model.response.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TencentImServiceImpl implements IMService {
    private String url = "https://%s/%s?sdkappid=%s&identifier=%s&usersig=%s&random=99999999&contenttype=json";
    private String domain = "adminapisgp.im.qcloud.com";
    // App 管理员帐号
    private String identifier = "system_bot";
    private static final Long IM_SDK_APP_ID = SdkConfig.IM_SDK_APP_ID;

    //创建 IM 客户端实例
    public ImClient client = ImClient.getInstance(SdkConfig.appId, SdkConfig.userId, SdkConfig.key, SdkConfig.domain);

    @Autowired
    private TLSSigAPIv2 tlsSigAPIv2;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private AuthUtil authUtil;
    @Autowired
    private RedisService redisService;

    @Override
    public IMParamsVo getImParams(GetImParamsDto dto) {
        AppCustomer appCustomer = appCustomerMapper.selectOne(new LambdaQueryWrapper<AppCustomer>().eq(AppCustomer::getId, dto.getCustomerId()));
        Asserts.fail(null == appCustomer, ResultCode.NOT_EXIST_CUSTOMER);

        String userId = String.valueOf(dto.getCustomerId());
        String userSig = tlsSigAPIv2.genUserSig(userId, CacheConstants.IM_USER_SIG_VALIDITY_SECONDS);
        IMParamsVo vo = new IMParamsVo();
        if (StringUtils.isNotBlank(userSig)) {
            vo.setUserSig(userSig);
            vo.setUserSigExpires(CacheConstants.IM_USER_SIG_VALIDITY_SECONDS);
        }

        String roomId = dto.getRoomId();
        long privilegeMap = dto.getPrivilegeMap();
        if (StringUtils.isNotBlank(roomId) && privilegeMap >= 0) {
            String privateMapKey = tlsSigAPIv2.genPrivateMapKeyWithStringRoomID(userId, CacheConstants.IM_USER_SIG_VALIDITY_SECONDS, roomId, privilegeMap);
            if (StringUtils.isNotBlank(privateMapKey)) {
                vo.setPrivateMapKey(privateMapKey);
                vo.setPrivateMapKeyExpires(CacheConstants.IM_USER_SIG_VALIDITY_SECONDS);
            }
        }
        return vo;
    }

    /**
     * 方法说明: 搜索群组
     * 传入参数:
     * 返回的值:
     * 作者: java-101
     * 日期: 2024-05-14 11:56:15
     */
    @Override
    public IMGroupListVo searchGroup(SearchGroupDto dto) {
        List<GroupInfo> groupInfoListEnd = Lists.newArrayList();

        String redisKey = "IM_CACHE_GROUP_LIST";

        //判断redis中是否存在
        boolean isOk = redisService.hasKey(redisKey);
        if (isOk) {
            //存在，查询redis里面的数据
            groupInfoListEnd = (List<GroupInfo>) redisService.get(redisKey);
        } else {
            List<GroupIdItem> groupIdItems = new ArrayList<>();//所有群组列表
            //递归查询所有群组
            getAppidGroupList(0L, groupIdItems);
            if (groupIdItems.size() > 0) {
                //有群组，查询群详情
                List<List<GroupIdItem>> parts = Lists.partition(groupIdItems, 50);//拆分成50个一组，批量查询群组详情
                for (List<GroupIdItem> groupIdItemList : parts) {
                    List<String> groupIdList = groupIdItemList.stream().map(GroupIdItem::getGroupId).collect(Collectors.toList());
                    if (ObjectUtil.isNotEmpty(groupIdList) && groupIdList.size() > 0) {
                        List<GroupInfo> groupInfoList = getGroupInfo(groupIdList);//批量查询群详情
                        if (ObjectUtil.isNotEmpty(groupInfoList) && groupInfoList.size() > 0) {
                            groupInfoListEnd.addAll(groupInfoList);
                        }
                    }
                }
                //按照创建时间进行排序 降序
                Collections.sort(groupInfoListEnd, Comparator.comparing(GroupInfo::getCreateTime, (t1, t2) -> t2.compareTo(t1)));
                //存入缓存
                if (groupInfoListEnd.size() > 0 && !redisService.hasKey(redisKey)) {
                    redisService.set(redisKey, groupInfoListEnd, 30);//有效期为5秒
                }
            }
        }

        //进行分页查询数据
        if (groupInfoListEnd.size() > 0) {

            if (ObjectUtil.isNotEmpty(dto.getSearchParam())) {
                //条件查询
                groupInfoListEnd = groupInfoListEnd.stream().filter(d -> d.getName().contains(dto.getSearchParam()) || d.getGroupId().contains(dto.getSearchParam())).collect(Collectors.toList());
            }

            IMGroupListVo<GroupInfo> pageUtil = new IMGroupListVo<>(groupInfoListEnd, dto.getPageNo(), dto.getPageSize());
            return pageUtil;
        } else {
            return new IMGroupListVo<>(dto.getPageNo(), dto.getPageSize());
        }
    }

    @Override
    public String sendSingleMessage(SendSingleMessageDto dto) {
        String uri = "v4/openim/sendmsg";
        JSONObject request = new JSONObject();

        JSONObject msgBody = new JSONObject();
        msgBody.put("MsgType", dto.getMsgType());
        msgBody.put("MsgContent", dto.getMsgContent());

        JSONArray msgBodyArr = new JSONArray();
        msgBodyArr.put(0, msgBody);

        request.put("SyncOtherMachine", 2);
        request.put("To_Account", dto.getToAccount());
        request.put("MsgRandom", RandomUtils.nextInt());
        request.put("MsgBody", msgBodyArr);
        request.put("CloudCustomData", "your cloud custom data");
        request.put("SupportMessageExtension", 0);

        String userSig = tlsSigAPIv2.genUserSig(identifier, CacheConstants.IM_USER_SIG_MESSAGE_VALIDITY_SECONDS);

        String fullUrl = String.format(url, domain, uri, IM_SDK_APP_ID, identifier, userSig);
        ResponseEntity<Map> resp = restTemplate.postForEntity(fullUrl, request.toString(), Map.class);
        Map respMap = resp.getBody();
        String actionStatus = (String) respMap.get("ActionStatus");
        String errorInfo = (String) respMap.get("ErrorInfo");
        if ("OK".equalsIgnoreCase(actionStatus)) {
            String msgKey = (String) respMap.get("MsgKey");
            return msgKey;
        } else {
            log.error("调用IM接口失败: {}, fullurl: {}", errorInfo, fullUrl);
            throw new ApiException(ResultCode.FAILED);
        }
    }

    @Override
    public SendBatchSingleMessageVo sendBatchSingleMessage(SendBatchSingleMessageDto dto) {
        String uri = "v4/openim/batchsendmsg";
        JSONObject request = new JSONObject();
        SendBatchSingleMessageVo vo =  new SendBatchSingleMessageVo();

        JSONObject msgBody = new JSONObject();
        msgBody.put("MsgType", dto.getMsgType());
        msgBody.put("MsgContent", dto.getMsgContent());

        JSONArray msgBodyArr = new JSONArray();
        msgBodyArr.put(0, msgBody);

        request.put("SyncOtherMachine", 2);
        request.put("To_Account", dto.getToAccountList());
        request.put("MsgRandom", RandomUtils.nextInt());
        request.put("MsgBody", msgBodyArr);

        String userSig = tlsSigAPIv2.genUserSig(identifier, CacheConstants.IM_USER_SIG_MESSAGE_VALIDITY_SECONDS);

        String fullUrl = String.format(url, domain, uri, IM_SDK_APP_ID, identifier, userSig);
        ResponseEntity<Map> resp = restTemplate.postForEntity(fullUrl, request.toString(), Map.class);
        Map respMap = resp.getBody();
        String actionStatus = (String) respMap.get("ActionStatus");
        String errorInfo = (String) respMap.get("ErrorInfo");
        List errorList = (List) respMap.get("ErrorList");
        if ("OK".equalsIgnoreCase(actionStatus)) {
            String msgKey = (String) respMap.get("MsgKey");
            vo.setMsgKey(msgKey);
        } else {
            log.error("调用IM接口失败: {}, fullurl: {}", errorInfo, fullUrl);
            vo.setErrorInfo(errorInfo);
            vo.setErrorList(errorList);
        }
        return vo;
    }

    /**
     * 方法说明: 添加好友-搜索用户资料
     */
    @Override
    public SearchCustomerVo searchCustomer(SearchCustomerDto dto) {
        Long loginId = authUtil.getUserId();

        SearchCustomerVo vo = new SearchCustomerVo();
        String searchWord = dto.getSearchWord();
        List<Long> idList = new ArrayList<>();
        if(StringUtils.isNumeric(searchWord)){
            // 如果是纯数字,假设是用id来查询
            AppCustomer appCustomer = appCustomerMapper.selectById(searchWord);
            if(appCustomer!=null) {
                idList.add(appCustomer.getId());
            }
        }
        // 通常按昵称来模糊查询
        LambdaQueryWrapper<AppCustomer> searchQuery = new LambdaQueryWrapper<>();
        searchQuery.like(AppCustomer::getNickname, searchWord);
        List<AppCustomer> custList = appCustomerMapper.selectList(searchQuery);
        if (!CollectionUtils.isEmpty(custList)) {
            List<Long> nickIdList = custList.stream().map(AppCustomer::getId).collect(Collectors.toList());
            idList.addAll(nickIdList);
        }
        if(CollectionUtils.isEmpty(idList)){
            return vo;
        }
        // 剔除自己
        idList.remove(loginId);

        if(idList.size()>100){
            idList = idList.subList(0,99);
        }
        List<String> userIdList = idList.stream().map(String::valueOf).collect(Collectors.toList());
        PortraitGetRequest request = PortraitGetRequest.builder()
                .tagList(dto.getTagList())
                .toAccount(userIdList)
                .build();
        PortraitGetResult result = null;
        try {
            result = client.profile.portraitGet(request);
        } catch (IOException e) {
            throw new ApiException(ResultCode.FAILED);
        }

        String actionStatus = result.getActionStatus();
        if(!"OK".equalsIgnoreCase(actionStatus)){
            throw new ApiException(ResultCode.FAILED, result.getErrorInfo());
        }
        List<UserProfileItem> items = result.getUserProfileItemList();
        if(!CollectionUtil.isEmpty(items)){
            List<IMCustomerVo> customerVos = new ArrayList<>();
            for(UserProfileItem up: items){
                Integer resultCode = up.getResultCode();
                if(resultCode==0) {
                    String userId = up.getToAccount();
                    AtomicReference<String> nicknameRef = new AtomicReference<>();
                    AtomicReference<String> avatarUrlRef = new AtomicReference<>();
                    List<ProfileItem> itemList = up.getProfileItemList();
                    if (!itemList.isEmpty()) {
                        itemList.forEach(pi -> {
                            if (TencentIMUserTagEnum.isNickname(pi.getTag())) {
                                nicknameRef.set((String) pi.getValue());
                            }
                            if (TencentIMUserTagEnum.isAvatarUrl(pi.getTag())) {
                                avatarUrlRef.set((String) pi.getValue());
                            }
                        });
                    }
                    String nickname = nicknameRef.get();
                    String avatarUrl = avatarUrlRef.get();
                    IMCustomerVo customerVo = new IMCustomerVo();
                    customerVo.setUserId(userId);
                    customerVo.setNickname(nickname);
                    customerVo.setAvatarUrl(avatarUrl);
                    customerVos.add(customerVo);
                }
            }
            vo.setImCustomerVoList(customerVos);
        }

        return vo;
    }

    @Override
    public List<String> pullFriends(PullFriendDto dto) {
        FriendGetRequest request = new FriendGetRequest();
        request.setFromAccount(dto.getUserId());
        request.setStartIndex(0);
        FriendGetResult result = null;
        try {
            result = client.sns.friendGet(request);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        if(!"OK".equalsIgnoreCase(result.getActionStatus())){
            throw new ApiException(ResultCode.FAILED, result.getErrorInfo());
        }
        List<UserDataItem> userDataItems = result.getUserDataItemList();
        if(CollectionUtil.isEmpty(userDataItems)){
            return List.of();
        }
        List<String> friendList = new ArrayList<>();
        List<String> userIdList = userDataItems.stream().map(UserDataItem::getToAccount).collect(Collectors.toList());
        friendList.addAll(userIdList);

        while(result.getCompleteFlag()==0){
            Integer nextStartIndex = result.getNextStartIndex();
            request.setStartIndex(nextStartIndex);
            try {
                result = client.sns.friendGet(request);
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                break;
            }
            userDataItems = result.getUserDataItemList();
            if(CollectionUtil.isEmpty(userDataItems)){
                break;
            }
            userIdList = userDataItems.stream().map(UserDataItem::getToAccount).collect(Collectors.toList());
            friendList.addAll(userIdList);
        }
        return friendList;
    }

    @Override
    public List<MemberResultItem> addGroupMember(IMAddGroupMemberDto dto) {
        AddGroupMemberRequest request = new AddGroupMemberRequest();
        request.setGroupId(dto.getGroupId());
        Integer silence = dto.getSilence();
        if(silence==null){
            silence = 0;
        }
        request.setSilence(silence);
        List<MemberRequestItem> itemList = dto.getMemberList().stream().map(m->{
            MemberRequestItem item = new MemberRequestItem();
            item.setMemberAccount(m);
            return item;
        }).collect(Collectors.toList());

        AddGroupMemberResult result = null;
        request.setMemberList(itemList);
        try {
            result = client.group.addGroupMember(request);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        if(!"OK".equalsIgnoreCase(result.getActionStatus())){
            throw new ApiException(ResultCode.FAILED, result.getErrorInfo());
        }
        List<MemberResultItem> memberResultItemList = result.getMemberList();
        if(CollectionUtil.isEmpty(memberResultItemList)){
            return List.of();
        }
        return memberResultItemList;
    }

    @Override
    public boolean messageCollect(IMMessageCollectDto dto) {
        return false;
    }

    @Override
    public IPage<IMMessageCollectVo> messageCollectPage(IMMessageCollectPageDto dto) {
        return null;
    }

    /**
     * 方法说明: 递归查询 腾讯云中 App 中的所有群组 群ID集合
     * 传入参数:  Next 第一次填0，后续填上一次返回的值，返回的 Next 为0时代表拉取完毕
     * 返回的值:
     * 作者: java-101
     * 日期: 2024-05-14 14:30:32
     */
    private List<GroupIdItem> getAppidGroupList(Long next, List<GroupIdItem> groupIdItems) {
        GetAppIdGroupListRequest request = GetAppIdGroupListRequest.builder()
                .limit(10000)
                .groupType(null)
                .next(next)
                .build();

        try {
            GetAppIdGroupListResult result = client.group.getAppIdGroupList(request);
            if (result.getTotalCount() > 0 && ObjectUtil.isNotEmpty(result.getGroupIdList())) {
                groupIdItems.addAll(result.getGroupIdList());
                if (result.getNext() != 0) {
                    Thread.sleep(1100);//接口限制为：1次/秒
                    getAppidGroupList(result.getNext(), groupIdItems);
                }
            }
        } catch (IOException e) {
            throw new ApiException(ResultCode.FAILED);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return groupIdItems;
    }

    /**
     * 方法说明: 批量-查询群详情信息
     * 传入参数:
     * 返回的值:
     * 作者: java-101
     * 日期: 2024-05-14 14:36:31
     */
    private List<GroupInfo> getGroupInfo(List<String> groupIdList) {
        GetGroupInfoRequest request = new GetGroupInfoRequest(groupIdList);
        try {
            GetGroupInfoResult result = client.group.getGroupInfo(request);
            System.out.println("1=======>>> " + result.getActionStatus());
            System.out.println("2=======>>> " + result.getGroupInfo().size());
            if (result.getActionStatus().equals("OK") && result.getGroupInfo().size() > 0) {
                return result.getGroupInfo();
            }
        } catch (IOException e) {
            throw new ApiException(ResultCode.FAILED);
        }
        return null;
    }


}
